#include "parsedcm.h"

#include "global.h"
#include "dcmtk/config/osconfig.h"
#include "dcmtk/dcmdata/dcfilefo.h"
#include "dcmtk/dcmdata/dcdatset.h"
#include "dcmtk/dcmdata/dcdeftag.h"

#include <QTimer>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlRecord>
#include <QCoreApplication>
#include <QDateTime>
#include <QDebug>

#define DB_CONN_NAME "PARSEDCM"

ParseDcm::ParseDcm(QObject *parent) : QObject(parent)
{
    openDb();
}

ParseDcm::~ParseDcm()
{
    QSqlDatabase::removeDatabase(DB_CONN_NAME);
}

void ParseDcm::openDb()
{
    if (!QSqlDatabase::database(DB_CONN_NAME).isOpen()) {
        QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", DB_CONN_NAME);
        db.setDatabaseName(QCoreApplication::applicationDirPath() + "/"SQLITE_DB_NAME);
        if (!db.open()) {
            qCritical() << "Open database failed: " << db.lastError().text();
        }
    }
}

void ParseDcm::fetchFile()
{
    gParseMutex.lock();
    if (gParseList.isEmpty())
        gParseCond.wait(&gParseMutex);
    QString f = gParseList.takeFirst();
    gParseMutex.unlock();
    if (!isDcmInDb(f)) parseDcm(f);
    QTimer::singleShot(0, this, SLOT(fetchFile()));
}

void ParseDcm::parseDcm(const QString &dcm)
{
    DcmFileFormat ff;
    OFCondition cond = ff.loadFile(dcm.toLocal8Bit().data());
    if (cond.good()) {
        DcmDataset *dset = ff.getDataset();
        OFString str;
        QString image_iuid, study_iuid;
        dset->findAndGetOFString(DCM_StudyInstanceUID, str);
        study_iuid = QString::fromLatin1(str.c_str());
        dset->findAndGetOFString(DCM_SOPInstanceUID, str);
        image_iuid = QString::fromLatin1(str.c_str());
        bool noFile;
        QSqlDatabase d = QSqlDatabase::database(DB_CONN_NAME);
        QSqlQuery q(d);
        if (!isImageInDb(image_iuid, noFile)) {
            if (!isStudyInDb(study_iuid)) {
                QString acc_num, modality, body_part;
                QString pati_id, pati_name, pati_sex, pati_age;
                QDate pati_birth;
                QDateTime study_time;
                dset->findAndGetOFString(DCM_AccessionNumber, str);
                acc_num = QString::fromLocal8Bit(str.c_str());
                dset->findAndGetOFString(DCM_Modality, str);
                modality = QString::fromLocal8Bit(str.c_str());
                dset->findAndGetOFString(DCM_BodyPartExamined, str);
                body_part = QString::fromLocal8Bit(str.c_str());
                dset->findAndGetOFString(DCM_StudyDate, str);
                study_time.setDate(QDate::fromString(QString::fromLatin1(str.c_str()).left(8), "yyyyMMdd"));
                dset->findAndGetOFString(DCM_StudyTime, str);
                study_time.setTime(QTime::fromString(QString::fromLatin1(str.c_str()).left(6), "hhmmss"));
                dset->findAndGetOFString(DCM_PatientID, str);
                pati_id = QString::fromLocal8Bit(str.c_str());
                dset->findAndGetOFString(DCM_PatientName, str);
                pati_name = QString::fromLocal8Bit(str.c_str());
                dset->findAndGetOFString(DCM_PatientSex, str);
                pati_sex = QString::fromLocal8Bit(str.c_str());
                dset->findAndGetOFString(DCM_PatientAge, str);
                pati_age = QString::fromLocal8Bit(str.c_str());
                if (pati_age.isEmpty()) {
                    dset->findAndGetOFString(DCM_PatientBirthDate, str);
                    pati_birth = QDate::fromString(QString::fromLatin1(str.c_str()).left(8), "yyyyMMdd");
                    QDate d = study_time.date();
                    if (d.isValid() && pati_birth.isValid()) {
                        //int days = pati_birth.daysTo(d);
                        if (d.year()-pati_birth.year()>0) {
                            pati_age = QString::number(d.year()-pati_birth.year()) + "Y";
                        } else if (d.month()-pati_birth.month()>0) {
                            pati_age = QString::number(d.month()-pati_birth.month()) + "M";
                        } else if (d.day()-pati_birth.day()>0) {
                            pati_age = QString::number(d.day()-pati_birth.day()) + "D";
                        }
                    }
                }

                q.prepare("INSERT INTO study_t(study_iuid, pati_id, pati_name, pati_sex,"
                          " pati_age, acc_num, modality, body_part, study_time) VALUES(?,?,?,?,?,?,?,?,?)");
                q.addBindValue(study_iuid);
                q.addBindValue(pati_id);
                q.addBindValue(pati_name);
                q.addBindValue(pati_sex);
                q.addBindValue(pati_age);
                q.addBindValue(acc_num);
                q.addBindValue(modality);
                q.addBindValue(body_part);
                q.addBindValue(study_time);
                if (!q.exec()) {
                    qCritical() << "sql insert: " << q.lastError().text();
                }
            }

            QString image_cuid, series_iuid;
            QDateTime image_time;
            dset->findAndGetOFString(DCM_SOPClassUID, str);
            image_cuid = QString::fromLatin1(str.c_str());
            dset->findAndGetOFString(DCM_SeriesInstanceUID, str);
            series_iuid = QString::fromLatin1(str.c_str());
            dset->findAndGetOFString(DCM_ContentDate, str);
            image_time.setDate(QDate::fromString(QString::fromLatin1(str.c_str()).left(8), "yyyyMMdd"));
            dset->findAndGetOFString(DCM_ContentTime, str);
            image_time.setTime(QTime::fromString(QString::fromLatin1(str.c_str()).left(6), "hhmmss"));

            q.prepare("INSERT INTO image_t(image_iuid, image_cuid, series_iuid, study_iuid_fk, image_time, file_path) VALUES(?,?,?,?,?,?)");
            q.addBindValue(image_iuid);
            q.addBindValue(image_cuid);
            q.addBindValue(series_iuid);
            q.addBindValue(study_iuid);
            q.addBindValue(image_time);
            q.addBindValue(dcm);
            if (!q.exec()) {
                qCritical() << "sql insert: " << q.lastError().text();
            }
        } else {
            if (noFile) {
                q.prepare("UPDATE image_t SET file_path=?, rm_f=0 WHERE image_iuid=?");
                q.addBindValue(dcm);
                q.addBindValue(image_iuid);
                if (!q.exec()) {
                    qCritical() << "sql update: " << q.lastError().text();
                } else {
                    qDebug() << "Updated file path to " << dcm << " where image iuid = " << image_iuid;
                }
            }
        }

    } else {
        qWarning() << "Load " << dcm << " failed: " << cond.text();
    }
}

void ParseDcm::onFileRemoved(const QString &file)
{
    QSqlDatabase d = QSqlDatabase::database(DB_CONN_NAME);
    QSqlQuery q(d);
    q.prepare("SELECT image_iuid FROM image_t WHERE file_path=?");
    q.addBindValue(file);
    if (!q.exec()) {
        qCritical() << "sql select: " << q.lastError().text();
    } else {
        while (q.next()) {
            QString uid = q.record().value("image_iuid").toString();
            q.prepare("UPDATE image_t SET file_path=NULL, rm_f=1 WHERE image_iuid=?");
            q.addBindValue(uid);
            if (!q.exec()) {
                qCritical() << "sql update: " << q.lastError().text();
            } else {
                qDebug() << "Remove file path of image iuid = " << uid;
            }
        }
    }
}

void ParseDcm::onFileRenamed(const QString &oldName, const QString &newName)
{
    QSqlDatabase d = QSqlDatabase::database(DB_CONN_NAME);
    QSqlQuery q(d);
    q.prepare("SELECT image_iuid FROM image_t WHERE file_path=?");
    q.addBindValue(oldName);
    if (!q.exec()) {
        qCritical() << "sql select: " << q.lastError().text();
    } else {
        while (q.next()) {
            QString uid = q.record().value("image_iuid").toString();
            q.prepare("UPDATE image_t SET file_path=? WHERE image_iuid=?");
            q.addBindValue(newName);
            q.addBindValue(uid);
            if (!q.exec()) {
                qCritical() << "sql update: " << q.lastError().text();
            } else {
                qDebug() << "Replaced " << oldName << " to " << newName << " where image iuid = " << uid;
            }
        }
    }
}

bool ParseDcm::isDcmInDb(const QString &dcm)
{
    //gDbLock.lockForRead();
    bool ret = false;
    QSqlDatabase d = QSqlDatabase::database(DB_CONN_NAME);
    QSqlQuery q(d);
    q.prepare("SELECT image_iuid FROM image_t WHERE file_path=?");
    q.addBindValue(dcm);
    if (q.exec() && q.next())
        ret = true;
    if (q.lastError().type() != QSqlError::NoError)
        qWarning() << "sql select: " << q.lastError().text();
    return ret;
    //gDbLock.unlock();
}

bool ParseDcm::isStudyInDb(const QString &uid)
{
    //gDbLock.lockForRead();
    bool ret = false;
    QSqlDatabase d = QSqlDatabase::database(DB_CONN_NAME);
    QSqlQuery q(d);
    q.prepare("SELECT study_iuid FROM study_t WHERE study_iuid=?");
    q.addBindValue(uid);
    if (q.exec() && q.next())
        ret = true;
    if (q.lastError().type() != QSqlError::NoError)
        qWarning() << "sql select: " << q.lastError().text();
    return ret;
    //gDbLock.unlock();
}

bool ParseDcm::isImageInDb(const QString &uid, bool &noFile)
{
    //gDbLock.lockForRead();
    bool ret = false;
    noFile = false;
    QSqlDatabase d = QSqlDatabase::database(DB_CONN_NAME);
    QSqlQuery q(d);
    q.prepare("SELECT image_iuid FROM image_t WHERE image_iuid=?");
    q.addBindValue(uid);
    if (q.exec() && q.next()) {
        noFile = q.record().value("file_path").toString().isEmpty();
        ret = true;
    }
    if (q.lastError().type() != QSqlError::NoError)
        qWarning() << "sql select: " << q.lastError().text();
    return ret;
    //gDbLock.unlock();
}
